package com.huaxonline.boot.log;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * 日志过滤器
 * 设置链路追踪ID到MDC，并在请求结束时清理
 * 
 * @author zhaoshulei
 * @since 2024-05-22
 */
@Slf4j
public class LogFilter implements Filter {

    @Autowired(required = false)
    private LogProperties logProperties;

    private static final String DEFAULT_TRACE_ID_HEADER = "X-Trace-Id";
    private static final String DEFAULT_TRACE_ID_FIELD = "traceId";

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.debug("LogFilter initialized");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String traceId = null;
        
        try {
            // 1. 获取或生成traceId
            traceId = getOrGenerateTraceId(httpRequest);
            
            // 2. 设置到MDC
            setTraceIdToMDC(traceId);
            
            // 3. 执行请求
            chain.doFilter(request, response);
            
        } finally {
            // 4. 清理MDC数据
            clearMdcData();
        }
    }

    @Override
    public void destroy() {
        log.debug("LogFilter destroyed");
    }

    /**
     * 获取或生成traceId
     */
    private String getOrGenerateTraceId(HttpServletRequest request) {
        // 1. 优先从请求头获取
        String traceIdHeader = getTraceIdHeader();
        String traceId = request.getHeader(traceIdHeader);
        if (StrUtil.isNotBlank(traceId)) {
            return traceId;
        }

        // 2. 从MDC获取（如果之前设置过）
        traceId = getTraceIdFromMDC();
        if (StrUtil.isNotBlank(traceId)) {
            return traceId;
        }

        // 3. 生成新的traceId
        return generateTraceId();
    }

    /**
     * 获取配置的traceId请求头字段名
     */
    private String getTraceIdHeader() {
        return logProperties != null ? logProperties.getTraceIdHeader() : DEFAULT_TRACE_ID_HEADER;
    }

    /**
     * 获取配置的traceId字段名
     */
    private String getTraceIdField() {
        return logProperties != null ? logProperties.getTraceIdField() : DEFAULT_TRACE_ID_FIELD;
    }

    /**
     * 从MDC获取traceId
     */
    private String getTraceIdFromMDC() {
        try {
            Class<?> mdcClass = Class.forName("org.slf4j.MDC");
            String fieldName = getTraceIdField();
            return (String) mdcClass.getMethod("get", String.class).invoke(null, fieldName);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 设置traceId到MDC
     */
    private void setTraceIdToMDC(String traceId) {
        try {
            Class<?> mdcClass = Class.forName("org.slf4j.MDC");
            String fieldName = getTraceIdField();
            mdcClass.getMethod("put", String.class, String.class).invoke(null, fieldName, traceId);
        } catch (Exception e) {
            log.debug("Failed to set traceId to MDC: {}", e.getMessage());
        }
    }

    /**
     * 清理MDC数据
     */
    private void clearMdcData() {
        try {
            Class<?> mdcClass = Class.forName("org.slf4j.MDC");
            mdcClass.getMethod("clear").invoke(null);
        } catch (Exception e) {
            log.debug("Failed to clear MDC data: {}", e.getMessage());
        }
    }

    /**
     * 生成新的traceId
     */
    private String generateTraceId() {
        // 使用多种方式生成traceId，确保唯一性
        long timestamp = System.currentTimeMillis();
        int random = (int) (Math.random() * 10000);
        return String.format("%d-%d-%s", timestamp, random, IdUtil.simpleUUID().substring(0, 8));
    }
}
